﻿using System;
using System.ComponentModel.Design;
using System.Diagnostics;
using System.Globalization;
using System.Linq;
using System.Runtime.InteropServices;
using System.Windows.Forms;
using EnvDTE;
using Microsoft.VisualStudio;
using Microsoft.VisualStudio.Shell;
using Microsoft.VisualStudio.Shell.Interop;
using TellagoInc.VsTagExplorer.TagSelector;

namespace TellagoInc.VsTagExplorer
{
    ///<summary>
    ///  This is the class that implements the package exposed by this assembly.
    ///
    ///  The minimum requirement for a class to be considered a valid package for Visual Studio
    ///  is to implement the IVsPackage interface and register itself with the shell.
    ///  This package uses the helper classes defined inside the Managed Package Framework (MPF)
    ///  to do it: it derives from the Package class that provides the implementation of the 
    ///  IVsPackage interface and uses the registration attributes defined in the framework to 
    ///  register itself and its components with the shell.
    ///</summary>
    // This attribute tells the PkgDef creation utility (CreatePkgDef.exe) that this class is
    // a package.
    [PackageRegistration(UseManagedResourcesOnly = true)]
    // This attribute is used to register the informations needed to show the this package
    // in the Help/About dialog of Visual Studio.
    [InstalledProductRegistration("#110", "#112", "1.0", IconResourceID = 400)]
    // This attribute is needed to let the shell know that this package exposes some menus.
    [ProvideMenuResource("Menus.ctmenu", 1)]
    // This attribute registers a tool window exposed by this package.
    [ProvideToolWindow(typeof (TagExplorerToolWindow))]
    [Guid(GuidList.guidVsTagExplorerPkgString)]
    public sealed class VsTagExplorerPackage : Package
    {
        /// <summary>
        ///   Default constructor of the package.
        ///   Inside this method you can place any initialization code that does not require 
        ///   any Visual Studio service because at this point the package object is created but 
        ///   not sited yet inside Visual Studio environment. The place to do all the other 
        ///   initialization is the Initialize method.
        /// </summary>
        public VsTagExplorerPackage()
        {
            Trace.WriteLine(string.Format(CultureInfo.CurrentCulture, "Entering constructor for: {0}", ToString()));
        }

        /// <summary>
        ///   This function is called when the user clicks the menu item that shows the 
        ///   tool window. See the Initialize method to see how the menu item is associated to 
        ///   this function using the OleMenuCommandService service and the MenuCommand class.
        /// </summary>
        private void ShowToolWindow(object sender, EventArgs e)
        {
            // Get the instance number 0 of this tool window. This window is single instance so this instance
            // is actually the only one.
            // The last flag is set to true so that if the tool window does not exists it will be created.
            var window = FindToolWindow(typeof (TagExplorerToolWindow), 0, true) as TagExplorerToolWindow;
            if ((null == window) || (null == window.Frame))
            {
                throw new NotSupportedException(Resources.CanNotCreateWindow);
            }
            var windowFrame = (IVsWindowFrame) window.Frame;
            ErrorHandler.ThrowOnFailure(windowFrame.Show());
        }


        /////////////////////////////////////////////////////////////////////////////
        // Overriden Package Implementation

        /// <summary>
        ///   Initialization of the package; this method is called right after the package is sited, so this is the place
        ///   where you can put all the initilaization code that rely on services provided by VisualStudio.
        /// </summary>
        protected override void Initialize()
        {
            Trace.WriteLine(string.Format(CultureInfo.CurrentCulture, "Entering Initialize() of: {0}", ToString()));
            base.Initialize();

            // Add our command handlers for menu (commands must exist in the .vsct file)
            var mcs = GetService(typeof (IMenuCommandService)) as OleMenuCommandService;
            if (null != mcs)
            {
                // Create the command for the menu item.
                var VsSetTagsCommandId = new CommandID(GuidList.guidVsTagExplorerCmdSet, (int) PkgCmdIDList.cmdidSetTags);
                var VsSetTags = new MenuCommand(VsSetTagsCallback, VsSetTagsCommandId);
                mcs.AddCommand(VsSetTags);

                // Create the command for the menu item.
                var VsEditTagsCommandId = new CommandID(GuidList.guidVsTagExplorerCmdSet, (int)PkgCmdIDList.cmdidEditTags);
                var VsEditTags = new MenuCommand(VsEditTagsCallback, VsEditTagsCommandId);
                mcs.AddCommand(VsEditTags);

                //Create the command for the menu item.
                var VsAddTagsCommandId = new CommandID(GuidList.guidVsTagExplorerCmdSet, (int) PkgCmdIDList.cmdidAddTags);
                var VsAddTags = new MenuCommand(VsAddTagsCallback, VsAddTagsCommandId);
                mcs.AddCommand(VsAddTags);

                // Create the command for the tool window
                var toolwndCommandID = new CommandID(GuidList.guidVsTagExplorerCmdSet,
                                                     (int) PkgCmdIDList.cmdidTagExplorer);
                var menuToolWin = new MenuCommand(ShowToolWindow, toolwndCommandID);
                mcs.AddCommand(menuToolWin);
            }
        }

        private static T GetService<T>() where T : class
        {
            return (T) ServiceProvider.GlobalProvider.GetService(typeof (T));
            //var componentModelService = (IComponentModel)ServiceProvider.GlobalProvider.GetService(typeof(SComponentModel));
            //return componentModelService.GetService<T>();
        }

        /// <summary>
        ///   This function is the callback used to execute a command when the a menu item is clicked.
        ///   See the Initialize method to see how the menu item is associated to this function using
        ///   the OleMenuCommandService service and the MenuCommand class.
        /// </summary>
        private void VsSetTagsCallback(object sender, EventArgs e)
        {
            var rootSelector = new TagsSelector();
            var envDte = GetService<DTE>();
            var solution = GetService<SVsSolution>();
            new SetTagsWorker(envDte, solution, rootSelector).Tag();
            rootSelector.Dispose();
        }

        /// <summary>
        ///   This function is the callback used to execute a command when the a menu item is clicked.
        ///   See the Initialize method to see how the menu item is associated to this function using
        ///   the OleMenuCommandService service and the MenuCommand class.
        /// </summary>
        private void VsEditTagsCallback(object sender, EventArgs e)
        {
            var rootSelector = new TagsSelector();
            var envDte = GetService<DTE>();
            var solution = GetService<SVsSolution>();
            if (envDte.SelectedItems.OfType<SelectedItem>().Count() > 1)
            {
                MessageBox.Show("You can edit tags of one file at a time.");
                return;
            }
            new EditTagsWorker(envDte, solution, rootSelector).Tag();
            rootSelector.Dispose();
        }

        /// <summary>
        ///   This function is the callback used to execute a command when the a menu item is clicked.
        ///   See the Initialize method to see how the menu item is associated to this function using
        ///   the OleMenuCommandService service and the MenuCommand class.
        /// </summary>
        private void VsAddTagsCallback(object sender, EventArgs e)
        {
            var rootSelector = new TagsSelector();
            var envDte = GetService<DTE>();
            var solution = GetService<SVsSolution>();
            new AddTagsWorker(envDte, solution, rootSelector).Tag();
            rootSelector.Dispose();
        }
    }
}